#include "processor.h"

typedef struct OptcProcessor {
    Processor *base;
} OptcProcessor;

GRegex *pid_rx;

static guint
optc_prov_node_hash_func (gconstpointer key) {
    char key_buf[1024];
    guint hash = 0;
    const ProvNode *p = key;
    GMatchInfo *match_info;
    gchar *pid;
    if (!g_strcmp0 (p->type, "Process")) {
        // if (g_regex_match (pid_rx, p->name, 0, &match_info)) {
        //         pid = g_match_info_fetch (match_info, 0);
        //         g_match_info_free (match_info);
        // }
        pid = strchr (p->name, ':');
        if (pid == NULL) {
            g_assert_not_reached ();
        }
        g_snprintf (key_buf, 1024, "%s_%s_%s",p->host,p->type,pid );
        return g_str_hash (key_buf);
    } else {
        g_snprintf (key_buf, 1024, "%s_%s_%s",p->host,p->type, p->name );
        return g_str_hash (key_buf);
    }
    return hash;
}

static gboolean
optc_prov_node_equal_func (gconstpointer a, gconstpointer b) {
    const ProvNode *pa = a;
    const ProvNode *pb = b;
    gchar *pid_a, *pid_b;
    GMatchInfo *match_info;
    if (!g_strcmp0 (pa->host, pb->host) && !g_strcmp0 (pa->type, pb->type)) {
        if (!g_strcmp0 (pa->type, "Process")) {
            pid_a = strchr (pa->name, ':');
            pid_b = strchr (pb->name, ':');
             if (pid_a == NULL || pid_b == NULL) {
                g_assert_not_reached ();
            }
            if (!g_strcmp0 (pid_a, pid_b))
                return TRUE;
        }else {
            if (!g_strcmp0 (pa->name, pb->name))
                return  TRUE;
        }
    }
    return FALSE;
}


int
optc_processor_process (void *opaque, Event *e) {
    OptcProcessor *op = opaque;
    Processor *p = op->base;
    ProvNode *from, *to, tmp_pn, *hint = &tmp_pn;
    if (!g_strcmp0 (e->source_type, "Port")) {
        hint->host = "(null)";
    } else {
        hint->host = e->host;
    }
    hint->name = e->source;
    hint->type = e->source_type;
    from = prov_node_find_active (p, hint);
    if (from == NULL) {
        if (!g_strcmp0 (e->source_type, "Port")) {
            from = prov_node_insert (p, "(null)", e->source_type, e->source);
        } else {
            from = prov_node_insert (p, e->host, e->source_type, e->source);
        }
    }
    if (!g_strcmp0 (e->target_type, "Port")) {
        hint->host = "(null)";
    } else {
        hint->host = e->host;
    }
    hint->name = e->target;
    hint->type = e->target_type;
    to = prov_node_find_active (p, hint);
    if (to == NULL) {
       if (!g_strcmp0 (e->target_type, "Port")) {
            to = prov_node_insert (p, "(null)", e->target_type, e->target);
        } else {
            to = prov_node_insert (p, e->host, e->target_type, e->target);
        }
    }
    if (!g_strcmp0 (from->type, "Process")) {
        if (strstr(from->name, "(null)") != NULL && strstr(e->source, "(null)") == NULL) {
            g_free (from->name);
            from->name = g_strdup (e->source);
        }
    }
    if (!g_strcmp0 (to->type, "Process")) {
        if (strstr(to->name, "(null)") != NULL && strstr(e->target, "(null)") == NULL) {
            g_free (to->name);
            to->name = g_strdup (e->target);
        }
    }
    prov_node_connect (p, from, to, e->type);

    if (e->remove_target) {
        prov_node_terminate (p, e->host, e->target_type, e->target);
    }

    return 0;
}

void
optc_processor_destructor (void *opaque) {
        OptcProcessor *optc_p = opaque;
        g_regex_unref (pid_rx);
        g_free (optc_p);
}

Processor *
optc_processor_create () {
    Processor *p;
    OptcProcessor *op;
    op = g_new0 (OptcProcessor, 1);
    if (op == NULL) return NULL;
    pid_rx = g_regex_new ("[^:]*$", G_REGEX_DEFAULT, G_REGEX_MATCH_DEFAULT, NULL);
    p = processor_create (op, optc_processor_destructor, optc_prov_node_hash_func, optc_prov_node_equal_func);
    p->process = optc_processor_process;
    op->base = p;
    return p;
}